Unit myPages;

  {$M+}
  {$H+}
  {$IFDEF FPC}
  {$MODE delphi}
  {$ENDIF}

Interface

Uses Classes, DateUtils, FileUtil, Forms, IniFiles, Dialogs,
    myFileUtils, myGpsThreads, myGraphics, myWinCELib, 
    Sdl, Sdl_image, Sdl_ttf, SysUtils, Windows;

Type
    THAlign = (halLeft, halCenter, halRight);
    TVAlign = (valTop, valMiddle, valBottom);
    TPos    = Record
        X, Y: Integer;
    End;

    TKeyMouseState      = (kmsUnknown, kmsMouseDown, kmsMouseUp,
        kmsMouseMove, kmsKeyPressed, kmsQuit);
    TKeyboardMouseEvent = Class(TObject)
    Private
        FMouseDownTiming: TDateTime;
        FMouseUpTiming: TDateTime;
        FMouseDownPos: TPos;
        FMouseUpPos: TPos;
        FMousePos: TPos;
        FEventState: TKeyMouseState;
        FKeyValue: DWORD;
    Public
        Constructor Create();
        Function InRect(myDisplayRect: TSDL_Rect): Boolean;
        Procedure HandleEvent(MyEvent: TSDL_Event);
        Property MouseDownTiming: TDateTime read FMouseDownTiming;
        Property MouseUpTiming: TDateTime read FMouseUpTiming;
//        Property MouseDownPos: TPos read FMouseDownPos write FMouseDownPos;
//        Property MouseUpPos: TPos read FMouseUpPos write FMouseUpPos;
//        Property MousePos: TPos read FMousePos write FMousePos;
        Property EventState: TKeyMouseState read FEventState;
        Property KeyValue: DWORD read FKeyValue;
    End;

    TTxtLabel = Class(TObject)
    Private
        FCaption: String;
        FHAlign: THAlign;
        FVAlign: TVAlign;
        FTextRect: TSDL_Rect;
        FFontSize: Integer;
        FStyle: Integer;
        FFontFile: String;
        FFgColor: TSDL_Color;
        FBgColor: TSDL_Color;
    Public
        Constructor Create();
        Function FontSizeText(): Boolean;
        Function DrawText(PaintBoard: PSDL_Surface): Boolean;
        Procedure LoadTextBox(Reader: TIniFile; Text_Sect: String);
        Procedure FullPath(BaseDir: String);
        Property HAlign: THAlign read FHAlign write FHAlign;
        Property VAlign: TVAlign read FVAlign write FVAlign;
    End;
    TTxtLabelArray = Array Of TTxtLabel;
  
    TTimeTag = Class(TTxtLabel)
    Private
        FTagArea: PSDL_Surface;
        FTimeFormat: String;
        FInterval: Integer;
        FCounter: Integer;
    Public
        Constructor Create();
        Destructor Destroy; Override;
        Function TagAreaBackup(PaintBoard: PSDL_Surface): Boolean;
        Function DisplayTag(PaintBoard: PSDL_Surface): Boolean;
        Procedure LoadTimeTag(Reader: TIniFile; Text_Sect: String);
    End;

    TTimeTagArray = Array Of TTimeTag;

    TGPSTag = Class(TTxtLabel)
    Private
        FTagArea: PSDL_Surface;
        FGPSFormat: String;
        FInterval: Integer;
        FCounter: Integer;
    Public
        Constructor Create();
        Destructor Destroy; Override;
        Function TagAreaBackup(PaintBoard: PSDL_Surface): Boolean;
        Function DisplayTag(PaintBoard: PSDL_Surface): Boolean;
        Procedure LoadGPSTag(Reader: TIniFile; Text_Sect: String);
    End;

    TGPSTagArray = Array Of TGPSTag;
  
    TIcon = Class(TObject)
    Private
        FDisplayRect: TSDL_Rect;
        FNormalRect: TSDL_Rect;
        FPushedRect: TSDL_Rect;
        FMouseDown: PSDL_Surface;
        FMouseUp: PSDL_Surface;
        FNormalAlpha: Integer;
        FPushedAlpha: Integer;
        FIconNormal: String;
        FIconPushed: String;
        FSection: String;
        FAction: String;
        FSound: String;
        FScreenSurface: PSDL_Surface;
        FFilename: String;
        FEntered: Boolean;
        FKey: Integer;
    Public
        Constructor Create(PaintBoard: PSDL_Surface);
        Destructor Destroy; Override;
        Function LoadIconImage(PaintBoard: PSDL_Surface): Boolean;
        Function HandleEvent(myEvent: TKeyboardMouseEvent): String;
        Procedure DisplayIconImage(PaintBoard: PSDL_Surface);
        Procedure LoadIconParam(Reader: TIniFile; Icon_Sect: String);
        Procedure SaveIconParam;
        Procedure FullPath(BaseDir: String);
    End;

    TIconArray  = Array Of TIcon;
    TImageFrame = Class(TObject)
    Private
        FBackupImg: PSDL_Surface;
        FFrames: PSDL_Surface;
        FFrameSrc: String;
        FFrameIndex: Integer;
        FLeft: Integer;
        FTop: Integer;
        FWidth: Integer;
        FHeight: Integer;
        FLen: Integer;
    Public
        Constructor Create;
        Destructor Destroy; Override;
        Procedure LoadFrame(Reader: TIniFile; Frame_Sect: String);
        Procedure FullPath(BaseDir: String);
        Function PrepareFrame(PaintBoard: PSDL_Surface): Boolean;
        Procedure DisplayFrame(PaintBoard: PSDL_Surface);
        Procedure PlayFrames(PaintBoard: PSDL_Surface; FrmIdx, ToIdx: Integer);
    End;

    TBattery = Class(TImageFrame)
    Private
        FAcPluged: Boolean;
        FWarn: Boolean;
    Public
        Constructor Create;
        Procedure LoadFrame(Reader: TIniFile; Battery_Sect: String); overload;
        Procedure DisplayBattery(PaintBoard: PSDL_Surface;
            Delayed: Boolean = True);
    End;

    TCompass = Class(TObject)
    Private
        FStaticRect: TSDL_Rect;
        FRotateRect: TSDL_Rect;
        FDisplayRect: TSDL_Rect;
        FStaticImg: PSDL_Surface;
        FRotateImg: PSDL_Surface;
        FBackupImg: PSDL_Surface;
        FIconStatic: String;
        FIconRotate: String;
        FColorKey: TSDL_Color;
        FClockwise: Boolean;
    Public
        Constructor Create;
        Destructor Destroy; Override;
        Procedure LoadCompass(Reader: TIniFile; Compass_Sect: String);
        Procedure FullPath(BaseDir: String);
        Function LoadCompassIcon: Boolean;
        Function PrepareCompass(PaintBoard: PSDL_Surface): Boolean;
        Procedure DisplayCompass(PaintBoard: PSDL_Surface; RotateDeg: Double);
    End;

    TTimeTagThread = Class(TThread)
    Private
        FTimeTagArray: TTimeTagArray;
        FScreenSurface: PSDL_Surface;
        Function GetStatus: Boolean;
    Public
        Constructor Create(PaintBoard: PSDL_Surface);
        Destructor Destroy; Override;
        Procedure Execute(); Override;
        Procedure ShrinkArray;
        Procedure ExtendArray;
        Procedure FreeResource;
        Property Enabled: Boolean read GetStatus;
        Property TimeTagArray: TTimeTagArray 
            read FTimeTagArray write FTimeTagArray;
    End;

    TBatteryThread = Class(TThread)
    Private
        FBatteryIcon: TBattery;
        FScreenSurface: PSDL_Surface;
        Function GetStatus: Boolean;
    Public
        Constructor Create(PaintBoard: PSDL_Surface);
        Destructor Destroy; Override;
        Procedure FreeResource;
        Procedure Execute(); Override;
        Property BatteryIcon: TBattery read FBatteryIcon write FBatteryIcon;
        Property Enabled: Boolean read GetStatus;
    End;

    TGPSTagThread = Class(TThread)
    Private
        FGPSTagArray: TGPSTagArray;
        FCompassIcon: TCompass;
        FScreenSurface: PSDL_Surface;
        Function GetStatus: Boolean;
    Public
        Constructor Create(PaintBoard: PSDL_Surface);
        Destructor Destroy; Override;
        Procedure FreeResource;
        Procedure Execute(); Override;
        Procedure ShrinkArray;
        Procedure ExtendArray;
        Property Enabled: Boolean read GetStatus;
        Property GPSTagArray: TGPSTagArray read FGPSTagArray write FGPSTagArray;
    End;

    TPageInfo = Class(TObject)
    Private
        FName: String;
        FBaseDir: String;
        FPageFile: String;
        FAnimation: String;
        FLoaded: Boolean;
        FInitialized: Boolean;
        FWidth: Integer;
        FHeight: Integer;
        FLeft: Integer;
        FTop: Integer;
        FDepth: Integer;
        FDeviceName: String;
        FBaudRate: Integer;
        FTimeZone: Double;
        FScreenSurface: PSDL_Surface;
        FPageSurface: PSDL_Surface;
        FTextArray: TTxtLabelArray;
        FIconArray: TIconArray;
        FTimeTagThread: TTimeTagThread;
        FGPSTagThread: TGPSTagThread;
        FBatteryThread: TBatteryThread;
        FCommandStr: String;
    Public
        Constructor Create;
        Destructor Destroy; Override;
        Function InitVideo: Boolean;
        Function InitEnvironment: Boolean;
        Procedure FreeResource();
        Procedure LoadPage();
        Procedure DisplayPage();
        Procedure Execute();
        Procedure ExtendTextArray;
        Procedure ExtendIconArray;
        Procedure ShrinkTextArray;
        Procedure ShrinkIconArray;
        Procedure HandleEvent;
        Property Name: String read FName write FName;
        Property Initialized: Boolean read FInitialized;
        Property Loaded: Boolean read FLoaded;
        Property CommandStr: String read FCommandStr write FCommandStr;    
    End;


Var
    ScreenLock: TRTLCriticalSection;
    GPSThread: TGPSThread;
    TerminalSig: Boolean;

Implementation

Constructor TKeyboardMouseEvent.Create();
Begin
    FMouseDownTiming := Now;
    FMouseUpTiming := Now;
    FMouseDownPos.X := -1;
    FMouseDownPos.Y := -1;
    FMouseUpPos.X := -1;
    FMouseUpPos.Y := -1;
    FMousePos.X := -1;
    FMousePos.Y := -1;
    FEventState := kmsUnknown;
    FKeyValue := 0;
End;

Function TKeyboardMouseEvent.InRect(myDisplayRect: TSDL_Rect): Boolean;
Begin
    With myDisplayRect Do If (FMousePos.X >= x) and (FMousePos.X <= x + w) and
            (FMousePos.Y >= y) and (FMousePos.Y <= y + h) Then Exit(True);
    Exit(False);
End;

Procedure TKeyboardMouseEvent.HandleEvent(MyEvent: TSDL_Event);
Begin
    Case MyEvent.Type_ Of
        SDL_QUITEV:
            Begin
                FEventState := kmsQuit;
            End;
        SDL_MouseButtonDown:
            Begin
                FMouseDownTiming := Now;
                FMouseDownPos.X := MyEvent.Button.X;
                FMouseDownPos.Y := MyEvent.Button.Y;
                FMousePos.X := FMouseDownPos.X;
                FMousePos.Y := FMouseDownPos.Y;
                FEventState := kmsMouseDown;
                FKeyValue := 0;
            End;
        SDL_MOUSEBUTTONUP:
            Begin
                FMouseUpTiming := Now;
                FMouseUpPos.X := MyEvent.Button.X;
                FMouseUpPos.Y := MyEvent.Button.Y;
                FMousePos.X := FMouseUpPos.X;
                FMousePos.Y := FMouseUpPos.Y;
                FEventState := kmsMouseUp;
                FKeyValue := 0;
            End;
        SDL_MOUSEMOTION:
            Begin
                FMousePos.X := MyEvent.Button.X;
                FMousePos.Y := MyEvent.Button.Y;
                FEventState := kmsMouseMove;
                FKeyValue := 0;
            End;
        SDL_KEYDOWN:
            Begin
                FEventState := kmsKeyPressed;
                FKeyValue := MyEvent.key.keysym.unicode;
            End;
    End;
End;

Constructor TTimeTagThread.Create(PaintBoard: PSDL_Surface);
Begin
    FreeOnTerminate := True;
    FScreenSurface := PaintBoard;
    SetLength(FTimeTagArray, 0);
    Inherited Create(True);
End;

Destructor TTimeTagThread.Destroy;
Begin
    FreeResource;
End;

Procedure TTimeTagThread.FreeResource;
Var
    i: Integer;
Begin
    If not Suspended Then Suspend;
    EnterCriticalSection(ScreenLock);
    For i := 0 To High(FTimeTagArray) Do FreeAndNil(FTimeTagArray[i]);  
    SetLength(FTimeTagArray, 0);
    LeaveCriticalSection(ScreenLock);
End;

Procedure TTimeTagThread.ExtendArray;
Begin
    SetLength(FTimeTagArray, Length(FTimeTagArray) + 1);    
    FTimeTagArray[High(FTimeTagArray)] := TTimeTag.Create;
End;

Procedure TTimeTagThread.ShrinkArray;
Begin
    FreeAndNil(FTimeTagArray[High(FTimeTagArray)]);
    SetLength(FTimeTagArray, High(FTimeTagArray));    
End;

Function TTimeTagThread.GetStatus: Boolean;
Begin
    If (Length(FTimeTagArray) = 0) Then Exit(False);
    Exit(True);
End;


Procedure TTimeTagThread.Execute();
Var
    I: Integer;
    St, Ed: DWORD;
Begin
    While not (Terminated or Suspended or TerminalSig) Do
    Begin
        St := GetTickCount();
        Application.ProcessMessages;
        For I := 0 To High(FTimeTagArray) Do
        Begin
            EnterCriticalSection(ScreenLock);
            If (Terminated or Suspended or TerminalSig) Then 
            Begin
                LeaveCriticalSection(ScreenLock);
                Exit;
            End;
            FTimeTagArray[I].DisplayTag(FScreenSurface);
            LeaveCriticalSection(ScreenLock);
        End;
        Ed := GetTickCount();
        While (Ed - St < 1000) and not (Terminated or Suspended or TerminalSig) Do
        Begin
            Sleep(10);
            If (Terminated or Suspended or TerminalSig) Then Exit;
            Ed := GetTickCount();
        End;
    End;
End;

Constructor TGPSTagThread.Create(PaintBoard: PSDL_Surface);
Begin
    FreeOnTerminate := True;
    FCompassIcon := Nil;
    FScreenSurface := PaintBoard;
    SetLength(FGPSTagArray, 0);
    Inherited Create(True);
End;

Destructor TGPSTagThread.Destroy;
Begin
    FreeResource;
End;

Procedure TGPSTagThread.FreeResource;
Var
    i: Integer;
Begin
    If not Suspended Then Suspend;
    EnterCriticalSection(ScreenLock);
    For i := 0 To High(FGPSTagArray) Do FreeAndNil(FGPSTagArray[i]);  
    SetLength(FGPSTagArray, 0);
    LeaveCriticalSection(ScreenLock);
End;

Function TGPSTagThread.GetStatus: Boolean;
Begin
    If (Length(FGPSTagArray) = 0) and (FCompassIcon = Nil) Then Exit(False);
    Exit(True);
End;

Procedure TGPSTagThread.ExtendArray;
Begin
    SetLength(FGPSTagArray, Length(FGPSTagArray) + 1);    
    FGPSTagArray[High(FGPSTagArray)] := TGPSTag.Create;
End;

Procedure TGPSTagThread.ShrinkArray;
Begin
    FreeAndNil(FGPSTagArray[High(FGPSTagArray)]);
    SetLength(FGPSTagArray, High(FGPSTagArray));    
End;

Procedure TGPSTagThread.Execute;
Var
    I: Integer;
    St, Ed: DWORD;
Begin
    While not (Terminated or Suspended or TerminalSig) Do
    Begin
        St := GetTickCount();
        Application.ProcessMessages;
        For I := 0 To High(FGPSTagArray) Do
        Begin
            EnterCriticalSection(ScreenLock);
            If (Terminated or Suspended or TerminalSig) Then 
            Begin
                LeaveCriticalSection(ScreenLock);
                Exit;
            End;
            FGPSTagArray[I].DisplayTag(FScreenSurface);
            LeaveCriticalSection(ScreenLock);
        End;
        Ed := GetTickCount();
        While (Ed - St < 800) and not (Terminated or Suspended or TerminalSig) Do
        Begin
            Sleep(10);
            If (Terminated or Suspended or TerminalSig) Then Exit;
            Ed := GetTickCount();
        End;
    End;
    Exit;
End;

Constructor TBatteryThread.Create(PaintBoard: PSDL_Surface);
Begin
    FreeOnTerminate := True;
    FScreenSurface := PaintBoard;
    FBatteryIcon := Nil;
    Inherited Create(True);
End;

Destructor TBatteryThread.Destroy;
Begin
    FreeResource;
End;

Function TBatteryThread.GetStatus: Boolean;
Begin
    Exit(FBatteryIcon <> Nil);
End;

Procedure TBatteryThread.FreeResource;
Begin
    If not Suspended Then Suspend;
    EnterCriticalSection(ScreenLock);
    If FBatteryIcon <> Nil Then FreeAndNil(FBatteryIcon);  
    LeaveCriticalSection(ScreenLock);
End;

Procedure TBatteryThread.Execute();
Var
    St, Ed: DWORD;
Begin
    While not (Terminated or Suspended or TerminalSig) Do
    Begin
        St := GetTickCount();
        Application.ProcessMessages;
        EnterCriticalSection(ScreenLock);
        If (Terminated or Suspended or TerminalSig) Then 
        Begin
            LeaveCriticalSection(ScreenLock);
            Exit;
        End;
        FBatteryIcon.DisplayBattery(FScreenSurface);
        LeaveCriticalSection(ScreenLock);
        Ed := GetTickCount();
        While (Ed - St < 1000) and not (Terminated or Suspended or TerminalSig) Do
        Begin
            Sleep(10);
            If (Terminated or Suspended or TerminalSig) Then Exit;
            Ed := GetTickCount();
        End;
    End;
    Exit;
End;

Destructor TPageInfo.Destroy;
Begin
    FreeResource;
    FreeAndNil(FTimeTagThread);
    FreeAndNil(FGPSTagThread); 
    FreeAndNil(FBatteryThread);
    FreeAndNil(GPSThread);
    If FScreenSurface <> Nil Then FreeSurface(FScreenSurface);
    DeleteCriticalSection(ScreenLock);
End;

Constructor TPageInfo.Create();
Begin
    FName := '';
    FBaseDir := '';
    FPageFile := '';
    FAnimation := 'NONE';
    FLoaded := False;
    FInitialized := False;
    FWidth := 0;
    FHeight := 0;
    FDepth := 32;
    FScreenSurface := Nil;
    FPageSurface := Nil;
    GPSThread := Nil;
    FTimeTagThread := Nil;
    FGPSTagThread := Nil;
    FBatteryThread := Nil;
    FCommandStr := '';
    SetLength(FTextArray, 0);
    SetLength(FIconArray, 0);
    FInitialized := InitEnvironment and InitVideo;
End;

Function TPageInfo.InitEnvironment: Boolean;
Var
    Reader: TIniFile;
    SkinName, SkinLoc: String;
    GPS_Setting: String;
Begin
    InitializeCriticalSection(ScreenLock);
    Reader := TIniFile.Create(ChangeFileExt(AppFullName, '.ini'), True);
    If Reader = Nil Then Exit(False);
    Reader.CaseSensitive := False;
    InitSinCos;
    SkinName := Reader.ReadString('SKIN', 'Name', 'default');
    SkinLoc := Reader.ReadString('SKIN', 'SkinDir', 'Skin');
    FWidth := Reader.ReadInteger('SCREEN', 'WIDTH', 10);
    FHeight := Reader.ReadInteger('SCREEN', 'HEIGHT', 10);
    FDepth := Reader.ReadInteger('SCREEN', 'DEPTH', 32);
    FDeviceName := UpperCase(Reader.ReadString('GPS', 'PORT', 'AUTO'));
    FBaudRate := Reader.ReadInteger('GPS', 'BAUD', 4800);
    FTimeZone := Reader.ReadFloat('GPS', 'TimeZone', 8.0);
    FBaseDir := MakeFullPath(SkinLoc + '\' + SkinName, AppLoc);
    FName := Reader.ReadString('SKIN', 'STARTUP', 'startup.ini');
    GPSThread := TGPSThread.Create(FDeviceName, FBaudRate);
    if GPSThread=nil then exit(false);
    GPS_Setting := Reader.ReadString('GPS', 'KM', '');
    If GPS_Setting <> '' Then GPSThread.KM := GPS_Setting;
    GPS_Setting := Reader.ReadString('GPS', 'KMH', '');
    If GPS_Setting <> '' Then GPSThread.KMH := GPS_Setting;
    GPS_Setting := Reader.ReadString('GPS', 'M', '');
    If GPS_Setting <> '' Then GPSThread.M := GPS_Setting;
    GPS_Setting := Reader.ReadString('GPS', 'MS', '');
    If GPS_Setting <> '' Then GPSThread.MS := GPS_Setting;
    GPS_Setting := Reader.ReadString('GPS', 'South', '');
    If GPS_Setting <> '' Then GPSThread.South := GPS_Setting;
    GPS_Setting := Reader.ReadString('GPS', 'West', '');
    If GPS_Setting <> '' Then GPSThread.West := GPS_Setting;
    GPS_Setting := Reader.ReadString('GPS', 'North', '');
    If GPS_Setting <> '' Then GPSThread.North := GPS_Setting;
    GPS_Setting := Reader.ReadString('GPS', 'East', '');
    If GPS_Setting <> '' Then GPSThread.East := GPS_Setting;
    GPSThread.TimeZone := Reader.ReadFloat('GPS', 'TimeZone', 8.0);

    Reader.Free;
    Exit(True);
End;

Function TPageInfo.InitVideo: Boolean;
Var
    Tmp: PSDL_Surface;
    Vid: PSDL_VideoInfo;
    VideoMode: DWORD;
Begin
    Vid := SDL_GetVideoInfo();
    VideoMode := SDL_SWSurface or SDL_NOFRAME or SDL_DOUBLEBUF or
        SDL_RLEACCEL or SDL_ASYNCBLIT;
    If FWidth * FHeight = 0 Then
    Begin
        FWidth  := Vid^.Current_w;
        FHeight := Vid^.Current_h;
    End;
    If (FWidth = Vid^.Current_w) and (FHeight = Vid^.Current_h) Then
        VideoMode := VideoMode or SDL_FULLSCREEN;
    If (FDepth <> Vid^.Vfmt^.BitsPerPixel) Then
        FDepth := Vid^.Vfmt^.BitsPerPixel;
    FScreenSurface := SDL_SetVideoMode(FWidth, FHeight, FDepth, VideoMode);
    If FScreenSurface = Nil Then Exit(False);
    Tmp := IMG_Load(PChar(MakeFullPath('Resource\logo_' +
        IntToStr(FWidth) + 'x' + IntToStr(FHeight) + '.png', AppLoc)));
    If Tmp <> Nil Then
    Begin
        BlitSurface(Tmp, Nil, FScreenSurface, Nil);
        SDL_UpdateRect(FScreenSurface, 0, 0, FWidth, FHeight);
        FreeSurface(Tmp);
    End;
    If FTimeTagThread = Nil Then FTimeTagThread :=
            TTimeTagThread.Create(FScreenSurface);
    If FGPSTagThread = Nil Then FGPSTagThread :=
            TGPSTagThread.Create(FScreenSurface);
    If FBatteryThread = Nil Then FBatteryThread :=
            TBatteryThread.Create(FScreenSurface);
    Exit(True);
End;

Procedure TPageInfo.FreeResource();
Var
    I: Integer;
Begin
    TerminalSig := True;
    If FTimeTagThread <> Nil Then 
    Begin
        FTimeTagThread.FreeResource;
    End;

    If FGPSTagThread <> Nil Then 
    Begin
        FGPSTagThread.FreeResource;
    End;

    If FBatteryThread <> Nil Then 
    Begin
        FBatteryThread.FreeResource;
    End;

    If not GPSThread.Suspended Then GPSThread.Suspend;

    For I := 0 To High(FIconArray) Do FreeAndNil(FIconArray[I]);
    SetLength(FIconArray, 0);
    For I := 0 To High(FTextArray) Do FreeAndNil(FTextArray[I]);
    SetLength(FTextArray, 0);
    If FPageSurface <> Nil Then FreeSurface(FPageSurface);
    FPageSurface := Nil;
    TerminalSig := False;
End;

Procedure TPageInfo.ExtendTextArray;
Begin
    SetLength(FTextArray, Length(FTextArray) + 1);    
    FTextArray[High(FTextArray)] := TTxtLabel.Create;  
End;

Procedure TPageInfo.ExtendIconArray;
Begin
    SetLength(FIconArray, Length(FIconArray) + 1);    
    FIconArray[High(FIconArray)] := TIcon.Create(FScreenSurface);
End;

Procedure TPageInfo.ShrinkTextArray;
Begin
    FreeAndNil(FTextArray[High(FTextArray)]);  
    SetLength(FTextArray, High(FTextArray));    
End;

Procedure TPageInfo.ShrinkIconArray;
Begin
    FreeAndNil(FIconArray[High(FIconArray)]);  
    SetLength(FIconArray, High(FIconArray));    
End;

Procedure TPageInfo.LoadPage();
Var
    Reader: TIniFile;
    SectList: TStringList;
    I: Integer;
    FileName: String;
    Sec_name: String;
    Rec_type: String;
Begin
    FreeResource;
    FLoaded := False;
    FileName := MakeFullPath(FName, FBaseDir);
    FBaseDir := ExcludeTrailingPathDelimiter(ExtractFilePath(FileName));
    FPageFile := FileName;
    Reader := TIniFile.Create(FileName, True);
    If Reader = Nil Then Exit;
    Reader.CaseSensitive := False;
    SectList := TStringList.Create;
    FileName := Reader.ReadString('PAGE', 'BACKGROUND', '');
    FAnimation := UpperCase(Reader.ReadString('PAGE', 'Animation', 'NONE'));
    FileName := MakeFullPath(FileName, FBaseDir);
    FPageSurface := MyGraphics.LoadImage(FileName);
    If FPageSurface = Nil Then FPageSurface :=
            SDL_CreateRGBSurface(SDL_SWSURFACE, FWidth, FHeight, FDepth, 0, 0, 0, 0);
    If FPageSurface = Nil Then 
    Begin
        FreeAndNil(Reader);
        Exit;
    End;
    Reader.ReadSections(SectList);
    For I := 0 To SectList.Count - 1 Do
    Begin
        Sec_name := UpperCase(SectList[I]);
        If Sec_name <> 'PAGE' Then
        Begin
            Rec_type := UpperCase(Reader.ReadString(Sec_name, 'TYPE', ''));
            If Sec_name = 'BATTERYICON' Then With FBatteryThread Do
                Begin
                    If BatteryIcon = Nil Then BatteryIcon := TBattery.Create;
                    BatteryIcon.LoadFrame(Reader, Sec_name);
                    BatteryIcon.FullPath(FBaseDir);
                    If BatteryIcon.PrepareFrame(FPageSurface) Then
                        BatteryIcon.DisplayBattery(FPageSurface, False)
                    Else
                    Begin
                        BatteryIcon.Free;
                        BatteryIcon := Nil;
                    End;
                    Continue;
                End;
            If Rec_type = 'TEXT' Then
            Begin
                ExtendTextArray;
                With FTextArray[High(FTextArray)] Do
                Begin
                    LoadTextBox(Reader, Sec_name);
                    If Trim(FCaption) = '' Then
                    Begin
                        ShrinkTextArray;
                        Continue;
                    End;
                    FullPath(FBaseDir);
                    If FontSizeText() = False Then
                    Begin
                        ShrinkTextArray;
                        Continue;
                    End;
                    If not DrawText(FPageSurface) Then ShrinkTextArray;
                End;
                Continue;
            End;
            If Rec_type = 'TIME' Then With FTimeTagThread Do
                Begin
                    ExtendArray;
                    With TimeTagArray[High(TimeTagArray)] Do
                    Begin
                        LoadTimeTag(Reader, Sec_name);
                        FullPath(FBaseDir);
                        If TagAreaBackup(FPageSurface) Then
                        Begin
                            FCounter := FInterval;
                            DisplayTag(FPageSurface);
                        End
                        Else ShrinkArray;
                    End;
                    Continue;
                End;
            If Rec_type = 'GPSTAG' Then With FGPSTagThread Do
                Begin
                    ExtendArray;
                    With GPSTagArray[High(GPSTagArray)] Do
                    Begin
                        LoadGPSTag(Reader, Sec_name);
                        FullPath(FBaseDir);
                        FCounter := FInterval;
                        If TagAreaBackup(FPageSurface) Then
                            DisplayTag(FPageSurface)
                        Else ShrinkArray;
                    End;
                    Continue;
                End;
            If Rec_type = 'ICON' Then
            Begin
                ExtendIconArray;
                With FIconArray[High(FIconArray)] Do
                Begin
                    LoadIconParam(Reader, Sec_name);
                    FullPath(FBaseDir);
                    If not LoadIconImage(FPageSurface) Then ShrinkIconArray;
                End;
                Continue;
            End;
        End;
    End;
    FreeAndNil(Reader);
    FreeAndNil(SectList);
    If not FGPSTagThread.Enabled Then FGPSTagThread.FreeResource;
    If not FTimeTagThread.Enabled Then FTimeTagThread.FreeResource;
    If not FBatteryThread.Enabled Then FBatteryThread.FreeResource;
    If FGPSTagThread.Enabled Then GPSThread.Resume;
    FLoaded := True;
End;

Procedure TPageInfo.HandleEvent;
Var
    GBEvent: TSDL_Event;
    myEvent: TKeyboardMouseEvent;
    i: Integer;
    CMD: String;
Begin
    myEvent := TKeyboardMouseEvent.Create;
    If SDL_WaitEvent(@GBEvent) = 0 Then Exit;
    myEvent.HandleEvent(GBEvent);
    If myEvent.EventState = kmsQuit Then
    Begin
        FCommandStr := '%QUIT%';
        FreeAndNil(myEvent);
        Exit;
    End;

    For i := 0 To High(FIconArray) Do
    Begin
        CMD := FIconArray[i].HandleEvent(myEvent);
        If CMD <> '' Then
        Begin
            FCommandStr := CMD;
            FreeAndNil(myEvent);
            Exit;
        End;
    End;
    FreeAndNil(myEvent);
End;

Procedure TPageInfo.DisplayPage();
Begin
    If FAnimation = 'FLYUP' Then BiltInUp(FScreenSurface, FPageSurface);
    If FAnimation = 'FLYDOWN' Then BiltInDown(FScreenSurface, FPageSurface);
    If FAnimation = 'FLYLEFT' Then BiltInLeft(FScreenSurface, FPageSurface);
    If FAnimation = 'FLYRIGHT' Then BiltInRight(FScreenSurface, FPageSurface);
    BlitSurface(FPageSurface, Nil, FScreenSurface, Nil);
    SDL_UpdateRect(FScreenSurface, 0, 0, FScreenSurface^.W, FScreenSurface^.H);
    If FPageSurface <> Nil Then
    Begin
        FreeSurface(FPageSurface);
        FPageSurface := Nil;
    End;
    If (FGPSTagThread.Enabled) and (FGPSTagThread.Suspended) Then
        FGPSTagThread.Resume;
    If (FTimeTagThread.Enabled) and (FTimeTagThread.Suspended) Then
        FTimeTagThread.Resume;
    If (FBatteryThread.Enabled) and (FBatteryThread.Suspended) Then
        FBatteryThread.Resume;
End;

Procedure TPageInfo.Execute();
Var
    Command_str: String;
    Parameter_str: String;
    FileName: String;
Begin
    Command_str := GetCommand(FCommandStr);
    Parameter_str := GetParameters(FCommandStr);
    If UpperCase(Command_str) = '%QUIT%' Then Exit;

    If UpperCase(Command_str) = '%LOAD%' Then
    Begin
        FPageFile := MakeFullPath(Parameter_str, FBaseDir);
        FBaseDir  := ExcludeTrailingPathDelimiter(ExtractFilePath(FPageFile));
        FName     := ExtractFileName(FPageFile);
        LoadPage();
        Exit;
    End;
    If UpperCase(Command_str) = '%RUNWAIT%' Then
    Begin
        Command_str   := GetCommand(Parameter_str);
        Parameter_str := GetParameters(Parameter_str);
        FileName      := MakeFullPath(Command_str, FBaseDir);
        If FileExistsUTF8(FileName) Then
        Begin
            FreeResource();
            OSExecuteWait(FileName, UTF16Str(Parameter_str));
            LoadPage();
            Exit;
        End;
        Exit;
    End;
  
    If UpperCase(Command_str) = '%RUN%' Then
    Begin
        Command_str   := GetCommand(Parameter_str);
        Parameter_str := GetParameters(Parameter_str);
        FileName      := MakeFullPath(Command_str, FBaseDir);
        If FileExistsUTF8(FileName) Then OSExecute(FileName, UTF16Str(Parameter_str));
        Exit;
    End;
End;

Constructor TTxtLabel.Create();
Begin
    FFgColor.R := 255;
    FFgColor.G := 255;
    FFgColor.B := 255;
    FBgColor.R := 0;
    FBgColor.G := 0;
    FBgColor.B := 0;
    FFontFile := MakeFullPath('Resource\font.ttf', AppLoc);
    FStyle := 0;
    FTextRect.X := 0;
    FTextRect.Y := 0;
    FFontSize := 12;
    FHAlign := halLeft;
    FVAlign := valTop;
End;

Procedure TTxtLabel.LoadTextBox(Reader: TIniFile; Text_Sect: String);
Var
    tmp: String;
Begin
    FTextRect.X := Reader.ReadInteger(Text_Sect, 'Left', 10);
    FTextRect.Y := Reader.ReadInteger(Text_Sect, 'Top', 10);
    FFontFile := Reader.ReadString(Text_Sect, 'Font', '%APPLOC%\Resource\font.ttf');
    FFontSize := Reader.ReadInteger(Text_Sect, 'Size', 12);
    FStyle := Reader.ReadInteger(Text_Sect, 'Style', 0) and $3f;
    FCaption := Reader.ReadString(Text_Sect, 'Text', '');
    tmp := UpperCase(Reader.ReadString(Text_Sect, 'VAlign', 'Top'));
    If tmp = 'MIDDLE' Then FVAlign := valMiddle;
    If tmp = 'BOTTOM' Then FVAlign := valBottom;
    tmp := UpperCase(Reader.ReadString(Text_Sect, 'HAlign', 'Left'));
    If tmp = 'RIGHT' Then FHAlign := halRight;
    If tmp = 'CENTER' Then FHAlign := halCenter;
    If Trim(Reader.ReadString(Text_Sect, 'Font', '')) <> '' Then
    FFontFile := Trim(Reader.ReadString(Text_Sect, 'Font', ''));
  
    FFgColor := TSDL_Color(StrToHex(UpperCase(Reader.ReadString(Text_Sect, 'Color', 'ffffff')), 1, 6));
    If Reader.ReadString(Text_Sect, 'Shadow', '') <> '' Then
    FBgColor := TSDL_Color(StrToHex(UpperCase(Reader.ReadString(Text_Sect, 'Shadow', '000000')), 1, 6))
    Else FBgColor := TSDL_Color($ffffff xor Uint32(FFgColor));
End;

Function TTxtLabel.FontSizeText(): Boolean;
Var
    Font: PTTF_FONT;
    Utf8_Caption: UTF8String;
    W, H: Integer;
Begin
    If (TTF_Init() = -1) Then Exit(False);
    Font := OpenTTFFont(FFontFile, FFontSize);
    If Font = Nil Then
    Begin
        CloseTTFFont(Font);
        TTF_Quit();
        Exit(False);
    End;
    TTF_SetFontStyle(Font, FStyle and 7);
    Utf8_Caption := UTF8Str(FCaption);
    W := 0;
    H := 0;
    TTF_SizeUTF8(Font, PChar(Utf8_Caption), W, H);
    FTextRect.H := H;
    FTextRect.W := W;
    CloseTTFFont(Font);
    TTF_Quit();
    If (FStyle and 8) > 0 Then
    Begin
        FStyle := FStyle and $0f;
        Inc(FTextRect.H, 2);
        Inc(FTextRect.W, 2);
    End;
    If (FStyle and 16) > 0 Then
    Begin
        FStyle := FStyle and $17;
        Inc(FTextRect.H);
        Inc(FTextRect.W);
    End;
    If (FStyle and 32) > 0 Then
    Begin
        FStyle := FStyle and $27;
        Inc(FTextRect.H, 5);
        Inc(FTextRect.W, 5);
    End;
    Exit(True);
End;

Procedure TTxtLabel.FullPath(BaseDir: String);
Begin
    FFontFile := MakeFullPath(FFontFile, BaseDir);
End;

Function TTxtLabel.DrawText(PaintBoard: PSDL_Surface): Boolean;
Var
    Font: PTTF_FONT;
    Utf8_Caption: UTF8String;
    TmpRect, ShadRect: SDL_Rect;
    Bg_text, Fg_text: PSDL_Surface;
    X, Y: Integer;
Begin
    If (TTF_Init() = -1) Then Exit(False);
    Utf8_Caption := UTF8Str(FCaption);
    Font := Nil;
    Font := OpenTTFFont(FFontFile, FFontSize);
    If Font = Nil Then
    Begin
        CloseTTFFont(Font);
        TTF_Quit();
        Exit(False);
    End;
    Fg_text := Nil;
    Bg_text := Nil;
    TTF_SetFontStyle(Font, FStyle and 7);
    Fg_text := TTF_RenderUTF8_Blended(Font, PChar(Utf8_Caption), FFgColor);
    If Fg_text = Nil Then
    Begin
        CloseTTFFont(Font);
        TTF_Quit();
        Exit(False);
    End;
    If FStyle > 7 Then
    Begin
        Bg_text := TTF_RenderUTF8_Blended(Font, PChar(Utf8_Caption), FBgColor);
        If Bg_text = Nil Then
        Begin
            FreeSurface(Fg_text);
            CloseTTFFont(Font);
            TTF_Quit();
            Exit(False);
        End;
    End;

    If (FStyle and 8) > 0 Then
    Begin
        If Bg_text = Nil Then Exit;
        ShadRect.X := 2 + FTextRect.X;
        ShadRect.Y := 2 + FTextRect.Y;
        ShadRect.H := Bg_text^.h;
        ShadRect.W := Bg_text^.w;
        Case FHAlign Of
            halRight: 
                ShadRect.X := PaintBoard^.w - Bg_text^.w;
            halCenter: 
                ShadRect.X := (PaintBoard^.w - Bg_text^.w + 1) div 2 + 2;
        End;
        Case FVAlign Of
            valBottom: 
                ShadRect.Y := PaintBoard^.h - Bg_text^.h;
            valMiddle: 
                ShadRect.Y := (PaintBoard^.h - Bg_text^.h + 1) div 2 + 2;
        End;
        BlitSurface(Bg_text, Nil, PaintBoard, @ShadRect);
    End;
    If (FStyle and 16) > 0 Then
    Begin
        For X := 0 To 2 Do For Y := 0 To 2 Do
            Begin
                If (X = 1) and (Y = 1) Then Continue;
                ShadRect.X := X + FTextRect.X;
                ShadRect.Y := Y + FTextRect.Y;
                ShadRect.H := Bg_text^.h;
                ShadRect.W := Bg_text^.w;
                Case FHAlign Of
                    halRight: 
                        ShadRect.X := PaintBoard^.w - Bg_text^.w - X;
                    halCenter: 
                        ShadRect.X := (PaintBoard^.w - Bg_text^.w + 1) div
                            2 - 1 + X;
                End;
                Case FVAlign Of
                    valBottom: 
                        ShadRect.Y := PaintBoard^.h - Bg_text^.h - Y;
                    valMiddle: 
                        ShadRect.Y := (PaintBoard^.h - Bg_text^.h + 1) div
                            2 - 1 + Y;
                End;
                BlitSurface(Bg_text, Nil, PaintBoard, @ShadRect);
            End;
    End;
    ShadRect.H := Fg_text^.H;
    ShadRect.W := Fg_text^.W;
    ShadRect.X := FTextRect.X;
    ShadRect.Y := FTextRect.Y;
    If (FStyle and 16) > 0 Then
    Begin
        Dec(ShadRect.H);
        Dec(ShadRect.W);
        Inc(ShadRect.X);
        Inc(ShadRect.Y);
    End;
    Case FHAlign Of
        halRight: 
            Begin
                ShadRect.X := PaintBoard^.w - Fg_text^.w -
                    (ShadRect.X - FTextRect.X);
                If (FStyle and 8) > 0 Then Dec(ShadRect.X, 2);
            End;
        halCenter: 
            ShadRect.X := (PaintBoard^.w - Bg_text^.w + 1) div 2;
    End;
    Case FVAlign Of
        valBottom: 
            Begin
                ShadRect.Y := PaintBoard^.h - Fg_text^.h -
                    (ShadRect.Y - FTextRect.Y);
                If (FStyle and 8) > 0 Then Dec(ShadRect.Y, 2);
            End;
        valMiddle: 
            ShadRect.Y := (PaintBoard^.h - Bg_text^.h + 1) div 2;
    End;
    BlitSurface(Fg_text, Nil, PaintBoard, @ShadRect);
    SDL_UpdateRect(PaintBoard, FTextRect.X, FTextRect.Y,
        FTextRect.W, FTextRect.H);
    If Bg_text <> Nil Then FreeSurface(Bg_text);
    If Fg_text <> Nil Then FreeSurface(Fg_text);
    CloseTTFFont(Font);
    TTF_Quit();
    Exit(True);
End;

Destructor TTimeTag.Destroy;
Begin
    If FTagArea <> Nil Then FreeSurface(FTagArea);
    Inherited Destroy;
End;

Constructor TTimeTag.Create();
Begin
    Inherited Create;
    FInterval := 500;
    FCounter := 0;
    FTagArea := Nil;
    FTimeFormat := 'HH":"NN":"SS';
End;

Procedure TTimeTag.LoadTimeTag(Reader: TIniFile; Text_Sect: String);
Begin
    LoadTextBox(Reader, Text_Sect);
    FInterval := Reader.ReadInteger(Text_Sect, 'Interval', 1);
    FTimeFormat := Reader.ReadString(Text_Sect, 'Format', 'HH":"NN":"SS');
End;

Function TTimeTag.TagAreaBackup(PaintBoard: PSDL_Surface): Boolean;
Var
    TmpTime: TDateTime;
Begin
    TmpTime := ComposeDateTime(EncodeDate(8888, 12, 28),
        EncodeTime(12, 59, 59, 999));
    FCaption := FormatDateTime(FTimeFormat, TmpTime);
    If not FontSizeText Then Exit(False);
    FTagArea := BackupSurface(PaintBoard, FTextRect);
    If FTagArea = Nil Then Exit(False);
    Exit(True);
End;

Function TTimeTag.DisplayTag(PaintBoard: PSDL_Surface): Boolean;
Var
    DisplayRect: TSDL_Rect;
    Tmp: PSDL_Surface;
Begin
    If (PaintBoard = Nil) or (FTagArea = Nil) Then Exit(False);
    Inc(FCounter, 1000);
    If FCounter < FInterval Then Exit(False);
    FCounter := 0;
    FCaption := FormatDateTime(FTimeFormat, Now);
    DisplayRect.X := FTextRect.X;
    DisplayRect.Y := FTextRect.Y;
    FTextRect.X := 0;
    FTextRect.Y := 0;
    Tmp := SDL_DisplayFormat(FTagArea);
    If Tmp = Nil Then Exit(False);
    If not DrawText(Tmp) Then
    Begin
        FTextRect.X := DisplayRect.X;
        FTextRect.Y := DisplayRect.Y;
        FreeSurface(Tmp);
        Exit(False);
    End;
    DisplayRect.W := FTextRect.W;
    DisplayRect.H := FTextRect.H;
    RestoreSurface(PaintBoard, Tmp, DisplayRect);
    FreeSurface(Tmp);
    FTextRect.X := DisplayRect.X;
    FTextRect.Y := DisplayRect.Y;
    Exit(True);
End;

Destructor TGPSTag.Destroy;
Begin
    If FTagArea <> Nil Then FreeSurface(FTagArea);
    Inherited Destroy;
End;

Constructor TGPSTag.Create();
Begin
    Inherited Create;
    FInterval := 500;
    FCounter := 0;
    FTagArea := Nil;
    FGPSFormat := '';
End;

Function TGPSTag.TagAreaBackup(PaintBoard: PSDL_Surface): Boolean;
Begin
    FCaption := GPSThread.PseudoMacro2GPS(FGPSFormat);
    If not FontSizeText Then Exit(False);
    FTagArea := BackupSurface(PaintBoard, FTextRect);
    If FTagArea = Nil Then Exit(False);
    Exit(True);
End;

Function TGPSTag.DisplayTag(PaintBoard: PSDL_Surface): Boolean;
Var
    DisplayRect: TSDL_Rect;
    Tmp: PSDL_Surface;
    Macro: String;
Begin
    If (PaintBoard = Nil) or (FTagArea = Nil) Then Exit(False);
    FCounter := 0;
    If GPSThread <> Nil Then
    Begin
        Macro := GPSThread.Macro2GPS(FGPSFormat);
        If Macro = FCaption Then Exit(True);
        FCaption := Macro; 
    End;
    DisplayRect.X := FTextRect.X;
    DisplayRect.Y := FTextRect.Y;
    FTextRect.X := 0;
    FTextRect.Y := 0;
    Tmp := SDL_DisplayFormat(FTagArea);
    If Tmp = Nil Then Exit(False);
    If not DrawText(Tmp) Then
    Begin
        FTextRect.X := DisplayRect.X;
        FTextRect.Y := DisplayRect.Y;
        FreeSurface(Tmp);
        Exit(False);
    End;
    RestoreSurface(PaintBoard, Tmp, DisplayRect);
    FreeSurface(Tmp);
    FTextRect.X := DisplayRect.X;
    FTextRect.Y := DisplayRect.Y;
    Exit(True);
End;

Procedure TGPSTag.LoadGPSTag(Reader: TIniFile; Text_Sect: String);
Begin
    LoadTextBox(Reader, Text_Sect);
    FInterval := Reader.ReadInteger(Text_Sect, 'Interval', 1000);
    FGPSFormat := Reader.ReadString(Text_Sect, 'Format', '');
End;

Destructor TIcon.Destroy;
Begin
    If FMouseDown <> Nil Then FreeSurface(FMouseDown);
    If FMouseUp <> Nil Then FreeSurface(FMouseUp);
    Inherited Destroy;
End;

Constructor TIcon.Create(PaintBoard: PSDL_Surface);
Begin
    FMouseDown := Nil;
    FMouseUp := Nil;
    FSection := '';
    FAction := '';
    FSound := '';
    FIconNormal := '';
    FIconPushed := '';
    FDisplayRect.X := 0;
    FDisplayRect.Y := 0;
    FDisplayRect.W := 0;
    FDisplayRect.H := 0;
    FNormalAlpha := 0;
    FPushedAlpha := 0;
    FEntered := False;
    FKey := 0;
    FScreenSurface := PaintBoard;
End;

Function TIcon.HandleEvent(myEvent: TKeyboardMouseEvent): String;
Var
    ofn: TOPENFILENAMEW;
Begin
    If myEvent.EventState = kmsKeyPressed Then
    Begin
        If FEntered Then
        Begin
            FEntered := False;
            DisplayIconImage(FScreenSurface);
            If (myEvent.KeyValue = FKey) Then
            Begin
                If FileExistsUTF8(FSound) Then PlaySound(FSound);
                Exit(FAction);
            End;
        End
        Else
        Begin
            If (myEvent.KeyValue <> FKey) Then Exit('');
            FEntered := True;
            DisplayIconImage(FScreenSurface);
            FEntered := False;
            If (FileExistsUTF8(FSound)) Then PlaySound(FSound);
            Exit(FAction);
        End;
        Exit('');
    End;

    If FEntered or myEvent.InRect(FDisplayRect) Then
    Begin
        If (FEntered = myEvent.InRect(FDisplayRect)) and
            (myEvent.EventState <> kmsMouseUp) Then Exit('');

        FEntered := myEvent.InRect(FDisplayRect);

        DisplayIconImage(FScreenSurface);

        If FEntered and (myEvent.EventState = kmsMouseUp) Then
        Begin
            FEntered := False;
            DisplayIconImage(FScreenSurface);
            If (FileExistsUTF8(FSound)) Then PlaySound(FSound);
            Exit(FAction);
        End;
    End;
    Exit('');
End;

Procedure TIcon.LoadIconParam(Reader: TIniFile; Icon_Sect: String);
Begin
    FSection := Icon_Sect;
    FFileName := Reader.FileName;
    FAction := Reader.ReadString(FSection, 'Action', '');
    FSound := Reader.ReadString(FSection, 'Sound', '');
    FIconNormal := Reader.ReadString(FSection, 'IconNormal', '');
    FIconPushed := Reader.ReadString(FSection, 'IconPushed', FIconNormal);
    FNormalAlpha := Reader.ReadInteger(FSection, 'NormalAlpha', 10);
    FPushedAlpha := Reader.ReadInteger(FSection, 'PushedAlpha', 10);
    FNormalRect.X := Reader.ReadInteger(FSection, 'Left', 10);
    FNormalRect.Y := Reader.ReadInteger(FSection, 'Top', 10);
    FNormalRect.W := Reader.ReadInteger(FSection, 'NormalSize', 16);
    FNormalRect.H := FNormalRect.W;
    FPushedRect.X := FNormalRect.X;
    FPushedRect.Y := FNormalRect.Y;
    FPushedRect.W := Reader.ReadInteger(FSection, 'PushedSize', FNormalRect.W);
    FPushedRect.H := FPushedRect.W;
    FDisplayRect.X := FNormalRect.X;
    FDisplayRect.Y := FNormalRect.Y;
    FDisplayRect.W := FNormalRect.W;
    FDisplayRect.H := FNormalRect.H;
    If FDisplayRect.W < FPushedRect.W Then
    Begin
        FDisplayRect.X := FPushedRect.X;
        FDisplayRect.Y := FPushedRect.Y;
        FDisplayRect.W := FPushedRect.W;
        FDisplayRect.H := FPushedRect.H;
    End;
    FNormalRect.X := (FDisplayRect.W - FNormalRect.W) div 2;
    FNormalRect.Y := (FDisplayRect.H - FNormalRect.H) div 2;
    FPushedRect.X := (FDisplayRect.W - FPushedRect.W) div 2;
    FPushedRect.Y := (FDisplayRect.H - FPushedRect.H) div 2;
End;

Procedure TIcon.SaveIconParam;
Var
    Writer: TIniFile;
Begin
    Writer := Nil;
    Writer := TIniFile.Create(FFilename, True);
    If Writer = Nil Then Exit;
    Writer.WriteString(FSection, 'Action', FAction);
    Writer.WriteString(FSection, 'Sound', FSound);
    Writer.WriteString(FSection, 'IconNormal', FIconNormal);
    Writer.WriteString(FSection, 'IconPushed', FIconPushed);
    Writer.WriteString(FSection, 'NormalAlpha', IntToStr(FNormalAlpha));
    Writer.WriteString(FSection, 'PushedAlpha', IntToStr(FPushedAlpha));
    Writer.WriteString(FSection, 'Left', IntToStr(FDisplayRect.X));
    Writer.WriteString(FSection, 'Top', IntToStr(FDisplayRect.Y));
    Writer.WriteString(FSection, 'NormalSize', IntToStr(FNormalRect.W));
    Writer.WriteString(FSection, 'PushedSize', IntToStr(FPushedRect.W));
    Writer.UpdateFile;
    FreeAndNil(Writer);
End;

Procedure TIcon.FullPath(BaseDir: String);
Begin
    FIconNormal := MakeFullPath(FIconNormal, BaseDir);
    FIconPushed := MakeFullPath(FIconPushed, BaseDir);
    FSound := MakeFullPath(FSound, BaseDir);
End;

Function TIcon.LoadIconImage(PaintBoard: PSDL_Surface): Boolean;
Var
    Tmp: PSDL_Surface;
Begin
    FMouseDown := BackupSurface(PaintBoard, FDisplayRect);
    If FMouseDown = Nil Then Exit(False);
    FMouseUp := BackupSurface(PaintBoard, FDisplayRect);
    If FMouseUp = Nil Then
    Begin
        FreeSurface(FMouseDown);
        Exit(False);
    End;
    Tmp := Nil;
    Tmp := LoadImageAlpha(FIconNormal);
    If Tmp = Nil Then
    Begin
        FreeSurface(FMouseDown);
        FreeSurface(FMouseUp);
        Exit(False);
    End;
    AlphaBlitSurface(Tmp, Nil, FMouseUp, @FNormalRect, FNormalAlpha);
    FreeSurface(Tmp);
    Tmp := LoadImageAlpha(FIconPushed);
    If Tmp = Nil Then
    Begin
        FreeSurface(FMouseDown);
        FreeSurface(FMouseUp);
        Exit(False);
    End;
    AlphaBlitSurface(Tmp, Nil, FMouseDown, @FPushedRect, FPushedAlpha);
    FreeSurface(Tmp);
    RestoreSurface(PaintBoard, FMouseUp, FDisplayRect);
    Exit(True);
End;

Procedure TIcon.DisplayIconImage(PaintBoard: PSDL_Surface);
Begin
    If FEntered Then
    Begin
        RestoreSurface(PaintBoard, FMouseDown, FDisplayRect);
        Sleep(30);
    End
    Else RestoreSurface(PaintBoard, FMouseUp, FDisplayRect);
End;

Destructor TImageFrame.Destroy;
Begin
    If FFrames <> Nil Then FreeSurface(FFrames);
    If FBackupImg <> Nil Then FreeSurface(FBackupImg);
    Inherited Destroy;
End;

Constructor TImageFrame.Create;
Begin
    FFrames := Nil;
    FLeft := 0;
    FTop := 0;
    FFrameSrc := '';
    FBackupImg := Nil;
    FFrameIndex := 0;
    FLen := 0;
End;

Procedure TImageFrame.LoadFrame(Reader: TIniFile; Frame_Sect: String);
Begin
    FFrameSrc := Reader.ReadString(Frame_Sect, 'FrameSource', FFrameSrc);
    FLeft := Reader.ReadInteger(Frame_Sect, 'Left', 10);
    FTop := Reader.ReadInteger(Frame_Sect, 'Top', 10);
End;

Procedure TImageFrame.FullPath(BaseDir: String);
Begin
    FFrameSrc := MakeFullPath(FFrameSrc, BaseDir);
End;

Function TImageFrame.PrepareFrame(PaintBoard: PSDL_Surface): Boolean;
Var
    RectTo: TSDL_Rect;
Begin
    FFrames := myGraphics.LoadImage(FFrameSrc);
    If FFrames = Nil Then Exit(False);
    RectTo.X := FLeft;
    RectTo.Y := FTop;
    FWidth := FFrames^.W;
    FHeight := FFrames^.H;
    RectTo.W := FWidth div FLen;
    RectTo.H := FHeight;
    If PaintBoard <> Nil Then FBackupImg := BackupSurface(PaintBoard, RectTo);
    If FBackupImg = Nil Then Exit(False);
    Exit(True);
End;

Procedure TImageFrame.DisplayFrame(PaintBoard: PSDL_Surface);
Var
    RectTo, RectFrom: TSDL_Rect;
    Temp: PSDL_Surface;
    Color: TSDL_Color;
Begin
    If TerminalSig Then Exit;
    If (FFrameIndex > FLen) or (FFrameIndex < 1) Then Exit;
    RectTo.X := FLeft;
    RectTo.Y := FTop;
    RectTo.W := FWidth div FLen;
    RectTo.H := FHeight;
    RectFrom.X := 0;
    RectFrom.Y := 0;
    RectFrom.W := RectTo.W;
    RectFrom.H := RectTo.H;
    Color.R := 0;
    Color.G := 0;
    Color.B := 0;
    Temp := SDL_DisplayFormat(FBackupImg);
    RectFrom.X := (FFrameIndex - 1) * RectTo.W;
    If FFrames = Nil Then Exit;
    TransparentColor(FFrames, Color);
    BlitSurface(FFrames, @RectFrom, Temp, Nil);
    RestoreSurface(PaintBoard, Temp, RectTo);
    FreeSurface(Temp);
End;

Procedure TImageFrame.PlayFrames(PaintBoard: PSDL_Surface;
    FrmIdx, ToIdx: Integer);
Var
    I: Integer;
Begin
    For I := FrmIdx To ToIdx Do
    Begin
        FFrameIndex := I;
        DisplayFrame(PaintBoard);
        If TerminalSig Then Exit;
        Sleep(20);
        If TerminalSig Then Exit;
    End;
End;

Constructor TBattery.Create;
Begin
    Inherited Create;
    FLen := 20;
    FAcPluged := False;
    FWarn := True;
    FFrameSrc := '%APPLOC%\Resource\Battery.bmp';
End;

Procedure TBattery.LoadFrame(Reader: TIniFile; Battery_Sect: String);
Begin
    Inherited;
    FWarn := Reader.ReadBool(Battery_Sect, 'Warn', True);
End;

Procedure TBattery.DisplayBattery(PaintBoard: PSDL_Surface;
    Delayed: Boolean = True);
Begin
    FAcPluged := False;
    If BatteryACPluged Then FAcPluged := True;
    FFrameIndex := BatteryPercent div 10;
    If FFrameIndex <= 1 Then 
    Begin
        FFrameIndex := 1;
        If FWarn Then PlaySound('');
    End;
    If FAcPluged Then
    Begin
        Inc(FFrameIndex, 10);
        If Delayed Then PlayFrames(PaintBoard, 11, FFrameIndex)
        Else DisplayFrame(PaintBoard);
    End
    Else DisplayFrame(PaintBoard);
End;

Destructor TCompass.Destroy;
Begin
    If FStaticImg <> Nil Then SDL_FreeSurface(FStaticImg);
    If FRotateImg <> Nil Then SDL_FreeSurface(FRotateImg);
    If FBackupImg <> Nil Then SDL_FreeSurface(FBackupImg);
    Inherited Destroy;
End;

Constructor TCompass.Create;
Begin
    FStaticImg := Nil;
    FRotateImg := Nil;
    FBackupImg := Nil;
    FIconRotate := '';
    FIconStatic := '';
    FRotateRect.X := 0;
    FRotateRect.Y := 0;
    FRotateRect.W := 0;
    FRotateRect.H := 0;
    FStaticRect.X := 0;
    FStaticRect.Y := 0;
    FStaticRect.W := 0;
    FStaticRect.H := 0;
End;

Procedure TCompass.LoadCompass(Reader: TIniFile; Compass_Sect: String);
Begin
    FIconRotate := Reader.ReadString(Compass_Sect, 'ROTATE', '');
    FIconStatic := Reader.ReadString(Compass_Sect, 'STATIC', '');
    FColorKey.R := StrToHex(UpperCase(Reader.ReadString(Compass_Sect,
        'ColorKey', '000000')), 1, 2);
    FColorKey.G := StrToHex(UpperCase(Reader.ReadString(Compass_Sect,
        'ColorKey', '000000')), 3, 2);
    FColorKey.B := StrToHex(UpperCase(Reader.ReadString(Compass_Sect,
        'ColorKey', '000000')), 5, 2);
    FClockwise := Reader.ReadBool(Compass_Sect, 'Clockwise', True);
    FRotateRect.X := Reader.ReadInteger(Compass_Sect, 'Left', 10);
    FRotateRect.Y := Reader.ReadInteger(Compass_Sect, 'Top', 10);
    FRotateRect.W := Reader.ReadInteger(Compass_Sect, 'RotateSize', 16);
    FRotateRect.H := FRotateRect.W;
    FStaticRect.X := FRotateRect.X;
    FStaticRect.Y := FRotateRect.Y;
    FStaticRect.W := Reader.ReadInteger(Compass_Sect, 'StaticSize',
        FRotateRect.W);
    FStaticRect.H := FStaticRect.W;
    If FIconStatic = '' Then
    Begin
        FStaticRect.X := 0;
        FStaticRect.Y := 0;
        FStaticRect.W := 0;
        FStaticRect.H := 0;
    End;
    FDisplayRect.X := FRotateRect.X;
    FDisplayRect.Y := FRotateRect.Y;
    FDisplayRect.W := FRotateRect.W;
    FDisplayRect.H := FRotateRect.H;
    If FDisplayRect.W < FStaticRect.W Then
    Begin
        FDisplayRect.X := FStaticRect.X;
        FDisplayRect.Y := FStaticRect.Y;
        FDisplayRect.W := FStaticRect.W;
        FDisplayRect.H := FStaticRect.H;
    End;
End;

Procedure TCompass.FullPath(BaseDir: String);
Begin
    FIconRotate := MakeFullPath(FIconRotate, BaseDir);
    If FIconStatic <> '' Then FIconStatic := MakeFullPath(FIconStatic, BaseDir);
End;

Function TCompass.LoadCompassIcon: Boolean;
Begin
    FRotateImg := LoadImageAlpha(FIconRotate);
    If FRotateImg = Nil Then Exit(False);
    If FIconStatic <> '' Then
    Begin
        FStaticImg := LoadImageAlpha(FIconRotate);
        If FStaticImg = Nil Then
        Begin
            FreeSurface(FRotateImg);
            Exit(False);
        End;
    End;
    Exit(True);
End;

Function TCompass.PrepareCompass(PaintBoard: PSDL_Surface): Boolean;
Begin
    If PaintBoard = Nil Then Exit(False);
    If not LoadCompassIcon Then Exit(False);
    FBackupImg := BackupSurface(PaintBoard, FDisplayRect);
    If FBackupImg = Nil Then Exit(False);
    If FStaticRect.W > FRotateRect.W Then
    Begin
        If FStaticImg <> Nil Then RestoreSurface(PaintBoard,
                FStaticImg, FStaticRect);
        RestoreSurface(PaintBoard, FRotateImg, FRotateRect);
    End
    Else
    Begin
        RestoreSurface(PaintBoard, FRotateImg, FRotateRect);
        If FStaticImg <> Nil Then RestoreSurface(PaintBoard,
                FStaticImg, FStaticRect);
    End;
    FRotateRect.X := (FDisplayRect.W - FRotateRect.W) div 2;
    FRotateRect.Y := (FDisplayRect.H - FRotateRect.H) div 2;
    FStaticRect.X := (FDisplayRect.W - FStaticRect.W) div 2;
    FStaticRect.Y := (FDisplayRect.H - FStaticRect.H) div 2;
    Exit(True);
End;

Procedure TCompass.DisplayCompass(PaintBoard: PSDL_Surface; RotateDeg: Double);
Var
    Tmp, Temp: PSDL_Surface;
    X, Y: Integer;
    Deg: Double;
Begin
    X := FRotateRect.X;
    Y := FRotateRect.Y;
    FRotateRect.X := 0;
    FRotateRect.Y := 0;
    Deg := RotateDeg;
    If FClockwise Then Deg := -Deg;
    Tmp := CreateColorSurface(FDisplayRect.W, FDisplayRect.H, FColorKey);
    If Tmp = Nil Then Exit;
    Temp := SDL_DisplayFormat(FBackupImg);
    If Temp = Nil Then Exit;
    If FStaticImg <> Nil Then TransparentColor(FStaticImg, FColorKey);
    TransparentColor(FRotateImg, FColorKey);
    If Round(Deg) <> 0 Then RotateSurfaceDeg(Tmp, FRotateImg, @FRotateRect,
            Round(FDisplayRect.W / 2 + 0.5),
            Round(FDisplayRect.H / 2 + 0.5),
            Round(FRotateRect.W / 2 + 0.5),
            Round(FRotateRect.H / 2 + 0.5), Round(Deg / 60 + 90))
    Else RestoreSurface(Tmp, FRotateImg, FRotateRect);
    FRotateRect.X := X;
    FRotateRect.Y := Y;
    TransparentColor(Tmp, FColorKey);
    If FRotateRect.W > FStaticRect.W Then
    Begin
        BlitSurface(Tmp, Nil, Temp, @FRotateRect);
        If FStaticImg <> Nil Then BlitSurface(FStaticImg, Nil,
                Temp, @FStaticRect);
    End
    Else
    Begin
        If FStaticImg <> Nil Then BlitSurface(FStaticImg, Nil,
                Temp, @FStaticRect);
        BlitSurface(Tmp, Nil, Temp, @FRotateRect);
    End;
    If Tmp <> Nil Then FreeSurface(Tmp);
    RestoreSurface(PaintBoard, Temp, FDisplayRect);
    If Temp <> Nil Then FreeSurface(Temp);
End;

End.
